/**************************
*@brief:  与web通信方法
*@version: v2.0
*@date: 2021/4/28 16:18
**************************/
package hanlder

import (
	"encoding/json"
	"fmt"
	"groudserver/config"
	"groudserver/db"
	"groudserver/file"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"

	"sync"

	"github.com/jasonlvhit/gocron"
	"github.com/sirupsen/logrus"
)

var (
	source      string
	fileHandler *file.FileHandler

	GlobalLimit = make(chan int)
	Port        string //端口号
	isWs        bool   //websocket 是否开启
	waitGroup   sync.WaitGroup
	ch          = make(chan struct{}, 1)
	Ptv         config.Cfg
)

// 初始化获取资源路径
func init() {
	JsonParse := config.NewJsonStruct()
	isWs = true
	Ptv = JsonParse.LoadCfg() //获取结构体信息
	source = Ptv.Dir + "/Source"
	//开启定时删除数据库中的内容
	fileHandler = file.NewFileStruct()
	Port = getPort(Ptv.Host) //获取端口号
	//开启定时任务
	go func() {
		s := gocron.NewScheduler()
		s.Every(3).Seconds().Do(TimeJob)
		<-s.Start()
	}()
}
func getPort(tracer string) string {
	comma := strings.Index(tracer, ":")
	index := strings.Index(tracer[comma:], "/")
	res := tracer[comma:]
	port := res[:index]
	logrus.Info("获取服务端口号:", port)
	return port
}
func (pv *playerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	logrus.Info("开始播放 play")
	var res Resp
	content := string(r.URL.Path[1:])
	index := strings.Index(content, "/")
	str := content[index+1:]
	path := source + "/" + str
	logrus.Info("ServeHTTP Path ", string(path))
	data, err := ioutil.ReadFile(string(path))
	if err != nil {
		logrus.Error("Error with path ", string(path), err)
		w.WriteHeader(404)
		w.Write([]byte("404"))
		res = GetStatusCode(404)
		response := &Resp{
			Code: res.Code,
			Msg:  res.Msg,
			Data: res.Data,
		}
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.Header().Set("Access-Control-Allow-Origin", "*")
		//跨域头
		w.Header().Set("Access-Control-Allow-Methods", "*")
		w.Header().Set("Access-Control-Allow-Headers", "*")
		if err := json.NewEncoder(w).Encode(response); err != nil {
			logrus.Fatal(err)
		}
		return
	}
	if strings.HasSuffix(path, ".html") {
		w.Header().Add("Content-Type", "text/html")
	} else if strings.HasSuffix(path, ".mp4") {
		w.Header().Add("Content-Type", "video/mp4")
	} else if strings.HasSuffix(path, ".png") {
		w.Header().Add("Content-Type", "image/png")
	} else if strings.HasSuffix(path, ".flv") {
		w.Header().Add("Content-Type", "video/flv")
	}
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Header().Set("Access-Control-Allow-Origin", "*")
	//跨域头
	w.Header().Set("Access-Control-Allow-Methods", "*")
	w.Header().Set("Access-Control-Allow-Headers", "*")
	w.Write(data)

}

/*
*************下载********************
1.下载和批量下载
2.请求参数{mid 和 id}
3.返回参数是body对应url
************************************
*/
func (pv *downloadHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	logrus.Info("正在开始下载.......")
	condition := []QueryBody{}
	tableData := make([]map[string]interface{}, 0)
	tabData := make(map[uint64]string)
	//存储子表id的map

	var res RespEx
	var sql string
	//token 鉴权校验
	if r.Method != "POST" {
		res = GetStatusCodeEx(405)
	}
	if r.RequestURI == "" {
		res = GetStatusCodeEx(406)
	} else if len(r.Header.Get("token")) > 0 && r.Header.Get("token") != "" {
		token := r.Header.Get("token")
		sql = "select username, token, createtime from " + Ptv.DataBase.UserInfoTbl + " where token = " + "'" + token + "'"
		res = VerifyToken(sql)
	}
	if res.Code == 200 { //如果鉴权成功
		body, err := ioutil.ReadAll(r.Body)
		if err != nil && err != io.EOF {
			logrus.Error("处理信息 err:", err.Error(), "信息请求处理消息体：", string(body))
			res = GetStatusCodeEx(400)
		} else {
			if err := json.Unmarshal([]byte(string(body)), &condition); err != nil {
				logrus.Error("解析失败:", err.Error())
				res = GetStatusCodeEx(406)
			} else {
				logrus.Info("下载数据请求数据的长度len:", len(condition))
				//初始的URl地址
				result := "http://" + Ptv.Host + "play"

				for index := 0; index < len(condition); index++ {
					if condition[index].Mid > 0 && condition[index].Id <= 0 {
						//下载的url地址
						var infoEx []*db.Alarm //查询子表的每一条信息
						subsql := "SELECT * FROM " + Ptv.DataBase.SubTbl + " where mid = " + fmt.Sprintf("%d", condition[index].Mid)
						logrus.Info("查询语句:", subsql)
						infoEx = QuerySubTblInfo(subsql)
						for i := 0; i < len(infoEx); i++ {
							req := regexp.MustCompile("[0-9]+")
							reqstr := req.FindAllString(infoEx[i].AlarmTime, -1)
							var strTime string
							for _, val := range reqstr {
								strTime += val
							}
							logrus.Info("对应的时间:", strTime, "AlarmMsg", infoEx[i].AlarmMsg)
							//先判断，文件是否存在,如果文件存在则返回正确url地址
							str := "/" + fmt.Sprintf("%d", infoEx[i].CarNum) + "/" + strTime + "/" + infoEx[i].AlarmMsg
							fileUrl := source + str
							isExist, _ := fileHandler.PathExists(fileUrl)
							if isExist {
								tabData[infoEx[i].Id] = result + str
							} else {
								continue
							}
						}

					} else if condition[index].Mid > 0 && condition[index].Id > 0 {
						//先判断附表是否有数据
						var info []*db.Alarm
						sql := "SELECT * FROM " + Ptv.DataBase.SubTbl + " where id = " + fmt.Sprintf("%d", condition[index].Id)
						info = QuerySubTblInfo(sql)
						logrus.Info("数据长度:", len(info))
						re := regexp.MustCompile("[0-9]+")
						str := re.FindAllString(info[0].AlarmTime, -1)
						var strTime string
						for _, val := range str {
							strTime += val
						}
						disStr := "/" + fmt.Sprintf("%d", info[0].CarNum) + "/" + strTime + "/" + info[0].AlarmMsg
						url := source + disStr
						isExist, _ := fileHandler.PathExists(url)
						if isExist {
							tabData[condition[index].Id] = result + disStr
						} else {
							logrus.Error("文件目录不存在 URL:", url)
							continue
						}

					}
				}

				for _, val := range tabData {
					entry := make(map[string]interface{})
					entry["url"] = val
					tableData = append(tableData, entry)
				}

				if len(tableData) == 0 {
					res = GetStatusCodeEx(404) //资源不存在存在
				} else {
					res.Data = tableData
					res.Msg = "ok"
				}
			}
		}
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Header().Set("Access-Control-Allow-Origin", "*")
	//跨域头
	w.Header().Set("Access-Control-Allow-Methods", "*")
	w.Header().Set("Access-Control-Allow-Headers", "*")
	if err := json.NewEncoder(w).Encode(res); err != nil {
		logrus.Fatal(err)
	}
}

/*******************下载URl*************************/
func (pv *downloadUrlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var res RespEx
	if r.Method != "POST" {
		res = GetStatusCodeEx(405)
	}
	if r.RequestURI == "" {
		res = GetStatusCodeEx(406)
	} else if len(r.Header.Get("token")) > 0 && r.Header.Get("token") != "" {
		token := r.Header.Get("token")
		sql := "select username, token, createtime from " + Ptv.DataBase.UserInfoTbl + " where token = " + "'" + token + "'"
		res = VerifyToken(sql)
	}
	if res.Code == 200 {
		body, err := ioutil.ReadAll(r.Body)
		if err != nil && err != io.EOF {
			logrus.Error("处理信息 err:", err.Error(), "信息请求处理消息体：", string(body))
			res = GetStatusCodeEx(400)
		} else {
			content := string(r.URL.Path[1:])
			index := strings.Index(content, "/")
			filePath := source + "/" + content
			logrus.Info("content:", content, "\nindex:", index, "\nfilePath:", filePath)
			//判断文件路径是否存在
			isExist, _ := fileHandler.PathExists(filePath)
			if isExist == true {
				file, _ := os.Open(filePath)
				defer file.Close()

				fileHeader := make([]byte, 512)
				file.Read(fileHeader)

				fileStat, _ := file.Stat()

				w.Header().Set("Content-Disposition", "attachment; filePath="+filePath) //filename
				w.Header().Set("Content-Type", http.DetectContentType(fileHeader))
				w.Header().Set("Content-Length", strconv.FormatInt(fileStat.Size(), 10))
				file.Seek(0, 0)
				io.Copy(w, file)
			} else {
				res = GetStatusCodeEx(404)
				w.Header().Set("Content-Type", "application/json; charset=utf-8")
				w.Header().Set("Access-Control-Allow-Origin", "*")
				//跨域头
				w.Header().Set("Access-Control-Allow-Methods", "*")
				w.Header().Set("Access-Control-Allow-Headers", "*")
				if err := json.NewEncoder(w).Encode(res); err != nil {
					logrus.Fatal(err)
				}
			}
		}
	}
}

/**************删除*******************/
func (pv *deleteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	condition := []QueryBody{}
	var res RespEx
	var sql string
	//token 鉴权校验
	if r.Method != "POST" {
		res = GetStatusCodeEx(405)
	}
	if r.RequestURI == "" {
		res = GetStatusCodeEx(406)
	} else if len(r.Header.Get("token")) > 0 && r.Header.Get("token") != "" {
		token := r.Header.Get("token")
		sql = "select username, token, createtime from " + Ptv.DataBase.UserInfoTbl + " where token = " + "'" + token + "'"
		res = VerifyToken(sql)
	}
	if res.Code == 200 { //如果鉴权成功
		body, err := ioutil.ReadAll(r.Body)
		if err != nil && err != io.EOF {
			logrus.Error("处理信息 err:", err.Error(), "信息请求处理消息体：", string(body))
			res = GetStatusCodeEx(400)
		} else {
			if err := json.Unmarshal([]byte(string(body)), &condition); err != nil {
				logrus.Error("解析失败:", err.Error())
				res = GetStatusCodeEx(406)
			} else {
				logrus.Info("请求数据长度len:", len(condition))
				for index := 0; index < len(condition); index++ {
					if condition[index].Mid > 0 && condition[index].Id <= 0 {
						var info []*db.AlarmData
						sql := "SELECT * FROM " + Ptv.DataBase.MainTbl + " where id = " + fmt.Sprintf("%d", condition[index].Mid)
						logrus.Info("查询数据接口：", sql)
						info = QueryMtlInfo(sql)
						if len(info) > 0 {
							re := regexp.MustCompile("[0-9]+")
							str := re.FindAllString(info[0].AlarmTime, -1)
							var strTime string
							for _, val := range str {
								strTime += val
							}
							del_dir := fmt.Sprintf("%d", info[0].CarNum) + "/" + strTime
							isdel := fileHandler.DeleteFile(del_dir, del_dir)
							logrus.Info("该目录下的文件：", del_dir, "已经删除", isdel)
							//删除数据中的内容
							sql = "delete from " + Ptv.DataBase.MainTbl + " where id= " + fmt.Sprintf("%d", condition[index].Mid)
							logrus.Info("删除主表sql:", sql)
							isdelmtl := db.DeleteDB(sql)
							if isdelmtl {
								sql = "delete from " + Ptv.DataBase.SubTbl + " where mid= " + fmt.Sprintf("%d", condition[index].Mid)
								isdelsubmtl := db.DeleteDB(sql)
								logrus.Info("删除附表sql语句：", sql)
								res = GetStatusCodeEx(200)
								logrus.Println("数据删除成功 isdelsubmtl", isdelsubmtl, "\n删除的车号: ", condition[index].CarNum, "\n删除文件路径:", del_dir)
							}
						}

					} else if condition[index].Mid > 0 && condition[index].Id > 0 {
						//1.先判断附表是否有数据
						var isdelmtl bool
						var info []*db.Alarm
						sql := "SELECT * FROM " + Ptv.DataBase.SubTbl + " where id = " + fmt.Sprintf("%d", condition[index].Id)
						info = QuerySubTblInfo(sql)
						for ii := 0; ii < len(info); ii++ {
							re := regexp.MustCompile("[0-9]+")
							str := re.FindAllString(info[ii].AlarmTime, -1)
							var strTime string
							for _, val := range str {
								strTime += val
							}
							dir := fmt.Sprintf("%d", info[0].CarNum) + "/" + strTime
							del_dir := dir + "/" + info[ii].AlarmMsg
							//删除本地中的数据
							isdel := fileHandler.DeleteFile(del_dir, dir)
							logrus.Println("先删除附表中的数据isdel:", isdel, "\n 删除路径:", dir, "\n 删除的信息:", info[ii].AlarmMsg)
							sql = "delete from " + Ptv.DataBase.SubTbl + " where id= " + fmt.Sprintf("%d", info[ii].Id)
							logrus.Info("删除附表sql:", sql)
							isdelsubmtl := db.DeleteDB(sql) //附表删除中的数据
							logrus.Println("删除本地数据isdelsubmtl:", isdelsubmtl)
							sql = "select COUNT(*) from " + Ptv.DataBase.SubTbl + " where mid= " + fmt.Sprintf("%d", info[ii].Mid)
							totalNumber := db.QueryToltalNumber(sql)
							if totalNumber == 0 {
								//删除主表中对应
								sql = "delete from " + Ptv.DataBase.MainTbl + " where id= " + fmt.Sprintf("%d", info[ii].Mid)
								isdelmtl = db.DeleteDB(sql)
								logrus.Info("删除主表的sql语句：", sql)
								logrus.Println("删除对应主表对应的信息，主表ID:", info[ii].Mid, "\n 删除主表：", isdelmtl)

							} else {
								logrus.Info("主表ID数据对应的数据大于0, totalNumber ", totalNumber)
							}

						}
					}

				}

				res = GetStatusCodeEx(200)
			}
		}
	}
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Header().Set("Access-Control-Allow-Origin", "*")
	//跨域头
	w.Header().Set("Access-Control-Allow-Methods", "*")
	w.Header().Set("Access-Control-Allow-Headers", "*")
	if err := json.NewEncoder(w).Encode(res); err != nil {
		logrus.Fatal(err)
	}
}

// 状态码信息设置
func GetStatusCode(code int) Resp {
	var res Resp
	if code == 200 {
		res.Code = 200
		res.Msg = "success"
	} else if code == 400 {
		res.Code = 400
		res.Data = false
		res.Msg = "Bad Request"
	} else if code == 404 {
		res.Code = 404
		res.Data = false
		res.Msg = "Not Found Resource"
	} else if code == 405 {
		res.Code = 405
		res.Data = false
		res.Msg = "Method not allowed"
	} else if code == 406 {
		res.Code = 406
		res.Data = false
		res.Msg = "Not Acceptable"
	}
	return res
}
func GetStatusCodeEx(code int) RespEx {
	var res RespEx
	if code == 200 {
		res.Code = 200
		res.Msg = "success"
		res.Data = true
	} else if code == 201 {
		res.Code = 201
		res.Msg = "token error" //token 错误
		res.Data = false
	} else if code == 202 {
		res.Code = 202
		res.Msg = "toke expire" //toke失效过期
		res.Data = false
	} else if code == 203 {
		res.Code = 203
		res.Data = false
		res.Msg = "not login"
	} else if code == 400 {
		res.Code = 400
		res.Data = false
		res.Msg = "bad request"
	} else if code == 404 {
		res.Code = 404
		res.Data = false
		res.Msg = "not found resource"
	} else if code == 405 {
		res.Code = 405
		res.Data = false
		res.Msg = "method not allowed"
	} else if code == 406 {
		res.Code = 406
		res.Data = false
		res.Msg = "not acceptable"
	}
	return res
}

// 判断interface是否为空
func IsNil(i interface{}) bool {

	vi := reflect.ValueOf(i)
	if vi.Kind() == reflect.Ptr {
		return vi.IsNil()
	}
	return false
}

/********************告警信息插入*********************/
//插入(车辆号，告警信息， 告警时间等)
func (pv *insertHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var res RespEx
	var isExist bool
	var sql string
	info := QueryBody{}
	if r.Method != "POST" {
		res = GetStatusCodeEx(405)
	}
	body, err := ioutil.ReadAll(r.Body)
	if err != nil && err != io.EOF {
		logrus.Error("处理信息失败 err：", err.Error(), "body:=", string(body))
		res = GetStatusCodeEx(406)
	} else {
		if err := json.Unmarshal([]byte(string(body)), &info); err != nil {
			res = GetStatusCodeEx(406)
		} else {
			logrus.Info("开始插入数据 CarNum:", info.CarNum, "AlarmTime:", info.AlarmTime, "AlarmMsg:", info.AlarmMsg)
			local, err := time.LoadLocation("Asia/Shanghai")
			if err != nil {
				local = time.FixedZone("CST", 8*3600)
			}
			alarmTime, _ := time.ParseInLocation("2006-01-02 15:04:05", info.AlarmTime, local)

			//查找主表中是否有对应的数据
			sql = "select count(*) from " + Ptv.DataBase.MainTbl + " where carNum= " + fmt.Sprintf("%d", info.CarNum) + " and alarmTime= '" + info.AlarmTime + "'"

			totalNumer := db.QueryToltalNumber(sql)
			logrus.Info("主表查询语句 sql：", sql, "  totalNumer:", totalNumer)
			if totalNumer == 0 {
				//插入数据库
				sql = "insert into " + Ptv.DataBase.MainTbl + "(carNum, alarmTime) values(?,?)"
				logrus.Info("插入主表中的sql：", sql)
				msgInfo := db.AlarmEx{
					CarNum:    info.CarNum,
					AlarmTime: alarmTime,
				}
				logrus.Info("插入主表数据sql", sql, "插入的消息:", msgInfo)
				isOK := db.InsertMtlDB(sql, msgInfo)
				//判断是否插入正确
				logrus.Info("插入是否正确:", isOK)
			}

			//查询主表Id,mid
			var alarmEx []*db.AlarmData
			sql = "select * from " + Ptv.DataBase.MainTbl + " where carNum= " + fmt.Sprintf("%d", info.CarNum) + " and alarmTime= '" + info.AlarmTime + "'"
			alarmEx, isExist = db.QueryAlarmEx(sql)

			if isExist { //如果主表数据存在，插入数据库
				//查看附表数据是否存在，不存在则插入
				res.Data = true //只要主表存在默认插入成功，保证客户端正常删除
				res.Code = 200
				res.Msg = "success"
				str := " and alarmMsg = '" + info.AlarmMsg + "'"
				sql = "select count(*) from " + Ptv.DataBase.SubTbl + " where carNum= " + fmt.Sprintf("%d", info.CarNum) + " and alarmTime= '" + info.AlarmTime + "'" + str
				totalNumer := db.QueryToltalNumber(sql)
				logrus.Info("附表统计语句:", sql)
				if totalNumer == 0 {
					msgInfo := db.AlarmInfo{
						Mid:       alarmEx[0].Id, //插入附表中的mid
						CarNum:    info.CarNum,
						AlarmTime: alarmTime,
						AlarmMsg:  info.AlarmMsg,
					}
					sql = "insert into " + Ptv.DataBase.SubTbl + "(mid, carNum, alarmTime, alarmMsg) values(?,?,?,?)"
					logrus.Info("插入附表中的sql：", sql)
					isOk := db.InsertSubTblDB(sql, msgInfo) //插入数据
					if isOk {
						//如果插入成功
						if strings.HasSuffix(info.AlarmMsg, ".mp4") && info.AlarmTime != "" {
							req := regexp.MustCompile("[0-9]+")
							reqstr := req.FindAllString(info.AlarmTime, -1)
							var strTime string
							for _, val := range reqstr {
								strTime += val
							}
							result := "http://" + Ptv.Host + "play"
							str := "/" + fmt.Sprintf("%d", info.CarNum) + "/" + strTime + "/" + info.AlarmMsg
							url := result + str
							Msg := WsResp{
								CarNum:    info.CarNum,
								AlarmTime: info.AlarmTime,
								MsgUrl:    url,
							}
							infoMsg, _ := json.Marshal(Msg)
							logrus.Info("发送的告警数据 msg:", string(infoMsg))
							WsMsg = string(infoMsg)
							GlobalLimit <- 5

						}
					} else {
						res.Code = 406
						res.Msg = "failed"
						res.Data = false
						logrus.Info("数据插入未成功.....")
					}
				}
			}
		}
	}

	if err := json.NewEncoder(w).Encode(res); err != nil {
		logrus.Fatal(err)
		return
	}
}

/*
******************************
1.查找查过3个月的主表数据
2.根据主表数据mid查找附表舒服
3.删除主表数据超过30天的数据
4.删除附表超过30天的数据
5.删除本地的真实数据
6.设置主表自增起始ID、设置附表自增起始ID
*******************************
*/
func TimeJob() {
	//1.查询3个月的数据
	var sql string
	var alarmInfo []*db.AlarmData
	var isExist bool
	sql = "SELECT * FROM " + Ptv.DataBase.MainTbl + " where alarmTime < DATE_SUB(NOW(), INTERVAL  " + Ptv.ExpireDate + " DAY)"
	alarmInfo, isExist = db.QueryAlarmEx(sql)
	if len(alarmInfo) > 0 && isExist { //数据存在
		logrus.Info("查询条件语句:", sql)
		for i := 0; i < len(alarmInfo); i++ {
			logrus.Info("定时查找数据的ID: ", alarmInfo[i].Id, "  查找数据车辆号：", alarmInfo[i].CarNum, " 查找数据告警时间：", alarmInfo[i].AlarmTime)
			req := regexp.MustCompile("[0-9]+")
			reqstr := req.FindAllString(alarmInfo[i].AlarmTime, -1)
			var strTime string
			for _, val := range reqstr {
				strTime += val
			}

			Dir := source + "/" + fmt.Sprintf("%d", alarmInfo[i].CarNum) + "/" + strTime
			isExist, _ = fileHandler.PathExists(Dir)
			if isExist {
				logrus.Info("文件存在,删除文件")
				del_dir := fmt.Sprintf("%d", alarmInfo[i].CarNum) + "/" + strTime
				isOk := fileHandler.DeleteFile(del_dir, del_dir)
				logrus.Info("本地资源文件删除 ", isOk)
			}
			sql = "delete from " + Ptv.DataBase.SubTbl + " where mid = " + fmt.Sprintf("%d", alarmInfo[i].Id)
			issubdel := db.DeleteDB(sql) // 删除附表记录
			sql = "delete from " + Ptv.DataBase.MainTbl + " where id = " + fmt.Sprintf("%d", alarmInfo[i].Id)
			ismdel := db.DeleteDB(sql)
			logrus.Info("附表删除成功:  ", issubdel, "主表删除成功:  ", ismdel)
		}
	}
}
